﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CommandGraph.Model;
using Eco.ObjectRepresentation;
using CommonClasses.Model;
using Eco.ObjectImplementation;
using EcoExtensions.Model;

namespace CommandGraph.Controls
{
    /// <summary>
    /// Редактор графов типа "Граф комманд" для IronHand IDE
    /// </summary>
    public partial class CommandsGraphEditorPlugin : UserControl, IDocumentEditor
    {
        #region Private fields

        /// <summary>
        /// Поставщик ECO сервисов (как правило, EcoSpace)
        /// </summary>
        private IEcoServiceProvider serviceProvider = null;

        #endregion

        #region Public properties

        /// <summary>
        /// Граф, который редактируется данным контролом
        /// </summary>
        public CommandsGraph Graph
        {
            get
            {
                return editor.Graph as CommandsGraph;
            }
            set
            {
                editor.Graph = value;
            }
        }
        /// <summary>
        /// Ящик с инструментами, используемый контролом
        /// </summary>
        public ToolBox ToolBox
        {
            get
            {
                return editor.ToolBox;
            }
        }

        #endregion

        public CommandsGraphEditorPlugin()
        {
            InitializeComponent();

            InitializePalette();

            editor.Canvas.Click += new EventHandler(Canvas_Click);
        }

        #region Public methods

        /// <summary>
        /// Указать контролу поставщик сервисов, с которым ему предстоит работать
        /// </summary>
        /// <param name="serviceProvider">
        /// Поставщик ECO-сервисов
        /// </param>
        public void Initialize(IEcoServiceProvider serviceProvider)
        {
            this.serviceProvider = serviceProvider;
            editor.Initialize(serviceProvider);
        }

        /// <summary>
        /// Скомпилировать граф в LPMED
        /// </summary>
        /// <param name="compilationUnit">
        /// Модуль, полученный при компиляции графа
        /// </param>
        /// <returns>
        /// Отчёт об ошибках компиляции 
        /// <remarks>
        /// (здесь присутствуют только ошибки предметной области, например - наличие циклов)
        /// </remarks>
        /// </returns>
        public ErrorReport Compile(out CommandsGraphCompilationUnit compilationUnit)
        {
            var code = string.Empty;
            var report = new ErrorReport(serviceProvider);

            compilationUnit = new CommandsGraphCompilationUnit(serviceProvider, this);

            #region Выключаем подсветку элементов с ошибками

            foreach (var entity in entitiesWithError.Values)
                entity.SetHighlighted(false);

            #endregion

            #region Собираем ошибки с элементов графа (ошибки в условиях)

            var editTimeCompiler = editor.ToolBox.GetToolByName("EditTimeCompilationTool") as EditTimeCompilationTool;

            foreach (var entity in Graph.Entities)
                if (entity is ExpressionVertex)
                    editTimeCompiler.ValidateExpression(entity as ExpressionVertex);
                else if (entity is ActiveTransition)
                    editTimeCompiler.ValidateExpression(entity as ActiveTransition);

            entitiesWithError = editTimeCompiler.GetErrors();

            foreach (var pair in entitiesWithError)
                report.Errors.Add(pair.Key);

            #endregion

            #region Генерируем код и собираем ошибки построения графа

            if (report.Errors.Count == 0)
            {
                var codeGenerator = new CodeGenerationTool(serviceProvider, compilationUnit);

                report = codeGenerator.GenCode(Graph, out code);

                foreach (var error in report.Errors)
                    entitiesWithError.Add(error, error.Tag.Object as GraphEntityBase);
            }

            for (int i = 0; i < report.Errors.Count; i++)
            {
                var error = report.Errors[i];
                error.CompilationUnit = compilationUnit;
                report.Errors.Add(error);
                error.Point.Point = new Point(0, i);
            }

            #endregion

            compilationUnit.LPMEDCode = code;

            return report;
        }

        #endregion

        #region Private methods

        private void InitializePalette()
        {
            palette.Items[0].Tag = "AddWaitVertexTool";
            palette.Items[1].Tag = "AddIfVertexTool";
            palette.Items[2].Tag = "AddLoopTool";
            palette.Items[3].Tag = "AddCommentVertexTool";
            palette.Items[4].Tag = "AddActiveTransitionTool";
            palette.Items[5].Tag = "AddCommentTransitionTool";
        }

        private void palette_Click(object sender, EventArgs e)
        {
            if (palette.SelectedItems.Count != 1)
                return;

            foreach (ListViewItem item in palette.Items)
                editor.ToolBox.DeactivateTool(item.Tag as string);

            editor.ToolBox.ActivateTool(palette.SelectedItems[0].Tag as string);
        }

        private void Canvas_Click(object sender, EventArgs e)
        {
            IsDirty = true;
        }

        #endregion

        #region IDocumentEditor Members

        #region Private fields used for IDocumentEditor realization

        /// <summary>
        /// Путь до файла, в который сохраняется данный граф
        /// </summary>
        private string path = string.Empty;
        /// <summary>
        /// Соответствие ошибок элементам графа
        /// </summary>
        private Dictionary<Error, GraphEntityBase> entitiesWithError = new Dictionary<Error, GraphEntityBase>();
        /// <summary>
        /// Показывает, были ли сделаны изменения в документе с момента последнего сохранения
        /// или создания
        /// </summary>
        private bool isDirty = false;

        #endregion

        #region Public properties

        /// <summary>
        /// Комментарий к редактору
        /// </summary>
        public string Comment { get; set; }

        /// <summary>
        /// Компилируется ли документ 
        /// </summary>
        public bool IsCompiled
        {
            get
            {
                return Graph.IsCompiled;
            }
            set
            {
                Graph.IsCompiled = value;
            }
        }

        /// <summary>
        /// Был ли документ изменен после последнего сохранения
        /// </summary>
        public bool IsDirty
        {
            get
            {
                return isDirty;
            }
            set
            {
                isDirty = value;
                if (OnDirtyChanged != null)
                    OnDirtyChanged(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Доступен ли документ только для чтения
        /// </summary>
        public bool IsReadOnly { get; set; }

        /// <summary>
        /// Путь до документа
        /// </summary>
        public string Path
        {
            get
            {
                return path;
            }
            set
            {
                var needResave = System.IO.File.Exists(path);
                path = value;

                //пересохранять будем только тогда, когда уже сохраняли
                if (needResave)
                    Rename(path);
            }
        }

        #endregion

        /// <summary>
        /// Дать меню для элемента документа
        /// <returns> 
        /// контекстное меню
        /// </returns>
        /// <param name="DocStructElement"> (IDocStructElement)элемент документа </param>
        public ContextMenuStrip GetDocElemMenu(IDocStructElement DocStructElement)
        {
            return new ContextMenuStrip();
        }

        /// <summary>
        /// Дать структуру документа 
        /// </summary>
        /// <returns>
        /// список элементов документа
        /// </returns>
        public IEcoList<EcoObject> GetDocStruct()
        {
            return new ObjectListAdapter<EcoObject>(new List<EcoObject>());
        }

        public void Initialize(Control control)
        {
            this.Parent = control;
            this.Dock = DockStyle.Fill;

            var painter = editor.ToolBox.GetToolByName("GraphPainterTool") as GraphPainterTool;
            foreach (var entity in Graph.Entities)
                painter.InvalidateEntity(entity);
        }

        public void OnDeselect()
        {
            return;
        }

        public void OnSelect()
        {
            return;
        }
        /// <summary>
        /// Переименовать документ
        /// </summary>
        /// <param name="NewName"> (string)новое имя </param>
        public void Rename(string NewName)
        {
            System.IO.File.Delete(Path);

            SaveAs(System.IO.Path.GetDirectoryName(Path) + NewName);
        }

        public void SaveAs(string path)
        {
            Path = System.IO.Path.GetDirectoryName(path) + System.IO.Path.DirectorySeparatorChar
                + System.IO.Path.GetFileNameWithoutExtension(path);

            (editor.ToolBox.GetToolByName("CommandsGraphLoaderTool") as CommandsGraphLoaderTool)
                .Save(Graph, path);

            IsDirty = false;
        }
        /// <summary>
        /// Подсветить в редакторе ошибку
        /// </summary>
        /// <param name="error"> (Error)ошибка </param>
        public void ShowError(Error error)
        {
            //нужно скинуть подсветку со всех элементов графа
            foreach (var errorEntity in entitiesWithError.Values)
                errorEntity.SetHighlighted(false);

            var original = entitiesWithError.Keys.FirstOrDefault(err => err.Point.Point == error.Point);
            if (original != null)
            {
                var entity = entitiesWithError[original];
                entity.SetHighlighted(true);

                var bounds = Rectangle.Round(entity.Region.GetBounds(editor.Canvas.CreateGraphics()));
                var entityRectAtScreen = new Rectangle(editor.Canvas.PointToScreen(bounds.Location),
                    bounds.Size);

                if (!editor.ClientRectangle.Contains(entityRectAtScreen))
                {
                    editor.HorizontalScroll.Value = Math.Max(editor.HorizontalScroll.Minimum,
                        Math.Min(editor.HorizontalScroll.Maximum * entityRectAtScreen.Left / editor.Canvas.Width,
                            editor.HorizontalScroll.Maximum));

                    editor.VerticalScroll.Value = Math.Max(editor.VerticalScroll.Minimum,
                        Math.Min(editor.VerticalScroll.Maximum * entityRectAtScreen.Top / editor.Canvas.Height,
                            editor.VerticalScroll.Maximum));
                }
            }
        }

        public void Verify()
        {
            throw new NotImplementedException("Oleg sayd this method is unused");
        }

        public event EventHandler OnDirtyChanged;

        public event EventHandler OnStrucChanged;

        #endregion
    }
}
